using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;

namespace XNALibrary
{
    public class RigidBody : IRenderable
    {
        #region Fields and properties

        // Shape
        protected Model model;
        protected Texture2D texture;
        protected float scale = 1.0f, scaleX = 1.0f, scaleY = 1.0f, scaleZ = 1.0f;

        // Position and linear velocity of CM
        protected Vector3 position = Vector3.Zero;
        protected Vector3 linearVelocity = Vector3.Zero;
        protected float friction;

        // Orientation and angular velocity
        protected Quaternion orientation = Quaternion.Identity;
        protected Matrix rotation = Matrix.Identity;
        protected Vector3 angularVelocity = Vector3.Zero;

        public CustomEffect effect;

        public RigidBody(Model m)
        {
            model = m;
            friction = .99f;
            scale = 1.0f;
            scaleX = 1.0f;
            scaleY = 1.0f;
            scaleZ = 1.0f;

            effect = null;
        }

        /// <param name="orentaion">{yaw, pitch, roll} in degrees</param>
        public RigidBody(Model m, Vector3 pos, Vector3 orientation)
            : this(m)
        {
            position = pos;

            rotation = Matrix.CreateRotationX(MathHelper.ToRadians(orientation.Y))
                * Matrix.CreateRotationY(MathHelper.ToRadians(orientation.X))
                * Matrix.CreateRotationZ(MathHelper.ToRadians(orientation.Z));
        }

        /// <param name="orentaion">{yaw, pitch, roll} in degrees</param>
        public void Rotate(float yaw, float pitch, float roll)
        {
            rotation *= Matrix.CreateRotationX(MathHelper.ToRadians(pitch))
                * Matrix.CreateRotationY(MathHelper.ToRadians(yaw))
                * Matrix.CreateRotationZ(MathHelper.ToRadians(roll));
        }

        public Model Model
        {
            get { return model; }
            set { model = value; }
        }

        public virtual Vector3 Position
        {
            get { return position; }
            set { position = value; }
        }

        public virtual Vector3 LinearVelocity
        {
            get { return linearVelocity; }
            set { linearVelocity = value; }
        }

        public virtual Vector3 AngularVelocity
        {
            get { return angularVelocity; }
            set { angularVelocity = value; }
        }

        public virtual Matrix Rotation
        {
            get { return rotation; }
            set { rotation = value; }
        }

        public float Scale
        {
            get { return scale; }
            set { scale = value; }
        }

        #endregion Fields and properties

        public virtual void Initialize()
        {

        }

        public Matrix GetWorld(ModelMesh mesh)
        {
            Matrix[] transforms = new Matrix[model.Bones.Count];
            model.CopyAbsoluteBoneTransformsTo(transforms);
            return transforms[mesh.ParentBone.Index]
                            * Matrix.CreateScale(scale)
                            * rotation
                            * Matrix.CreateTranslation(Position);
        }
        
        public virtual void Update()
        {
            position += linearVelocity;

            // Apply friction, i.e decay velocity
            linearVelocity *= friction;
        }

        public virtual void Reset()
        {
            position = Vector3.Zero;
            linearVelocity = Vector3.Zero;

            orientation = Quaternion.Identity;
            rotation = Matrix.Identity;
            angularVelocity = Vector3.Zero;
        }

        public virtual void Render( RenderControl rc, GameTime gt)
        {
            if (effect != null)
                effect.DrawEffect(this, rc);
            else
            {
                if (model == null) return;

                // Draw the model. A model can have multiple meshes, so loop.
                foreach (ModelMesh mesh in model.Meshes)
                {
                    // This is where the mesh orientation is set, as well as our camera and projection.
                    foreach (BasicEffect _effect in mesh.Effects)
                    {
                        //effect.EnableDefaultLighting();
                        _effect.World = GetWorld(mesh);
                        _effect.View = rc.CurrentCamera.View;
                        _effect.Projection = rc.CurrentCamera.Projection;
                        if (texture != null)
                        {
                            _effect.Texture = texture;
                            _effect.TextureEnabled = true;
                        }
                    }
                    // Draw the mesh, using the effects set above.
                    mesh.Draw();
                }
            }
        }

    }
}
